import { DirectiveBinding, Directive } from "vue";
import { ObserverEvent } from "@/utils";
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
type NodeState = Map<
    HTMLElement,
    {
        documentHandler: DocumentHandler;
        bindingFn: (...args: unknown[]) => unknown;
    }[]
>;
const nodeList: NodeState = new Map();
let startClick: MouseEvent;

ObserverEvent(document, "mousedown", (el: MouseEvent) => {
    startClick = el;
});
ObserverEvent(document, "mouseup", (el: MouseEvent) => {
    for (const handlers of nodeList.values()) {
        for (const { documentHandler } of handlers) {
            documentHandler(el, startClick);
        }
    }
});

const createDocumentHandler = (
    el: HTMLElement,
    binding: DirectiveBinding
): DocumentHandler => {
    return (mouseup, mousedown) => {
        const mouseUpTarget = mouseup.target as Node;
        const mouseDownTarget = mousedown?.target as Node;
        const isContainedByEl =
            el.contains(mouseUpTarget) || el.contains(mouseDownTarget);
        if (isContainedByEl) {
            return;
        }
        binding.value(mouseup, mousedown);
    };
};
const ClickOutside: Directive = {
    beforeMount(el, binding) {
        if (!nodeList.has(el)) {
            nodeList.set(el, []);
        }
        const _cur = nodeList.get(el);
        _cur &&
            _cur.push({
                documentHandler: createDocumentHandler(el, binding),
                bindingFn: binding.value
            });
    },
    updated(el, binding) {
        if (!nodeList.has(el)) {
            nodeList.set(el, []);
        }

        const handlers = nodeList.get(el);
        if (!handlers) return;
        const oldHandlerIndex = handlers.findIndex(
            item => item.bindingFn === binding.oldValue
        );
        const newHandler = {
            documentHandler: createDocumentHandler(el, binding),
            bindingFn: binding.value
        };

        if (oldHandlerIndex >= 0) {
            handlers.splice(oldHandlerIndex, 1, newHandler);
        } else {
            handlers.push(newHandler);
        }
    },
    unmounted(el) {
        nodeList.delete(el);
    }
};

export default ClickOutside;
